Amazon API Gatewayのスロットリングについて調査してみた
AWS事業本部木村です。
API GatewayからLambdaを呼び出す際のスロットリングに関して調べる機会があったので、まとめてみようと思います。
初めに
今回はREST APIを対象として、調査を行なっています。HTTP APIではAPIキーと使用量プランが対象外などの差分がありますので参考にされる場合はご注意ください。
利用できる機能の差分に関しては以下をご参照ください。
https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/http-api-vs-rest.html
API Gatewayで設定できるスロットリングに関して
API Gatewayでは、以下の3つの観点からスロットリングの設定が可能です。
・アカウント全体のリージョンに対するスロットリング
・APIキーと使用量プランに基づくスロットリング
・アクセス全体に対するメソッドレベルでのスロットリング
使用量プランを用いたクォータを除いては1秒あたりのリクエストを基準にスロットリングを行います。
1秒間あたりの設定値を超えたら、超えたリクエストがただちに制限されるわけではなくバーストに設定された値に応じて制限が緩和されます。
詳細の説明に関しては以下の記事がわかりやすかったので、ご参照ください。
アカウント全体のリージョンに対するスロットリング
こちらはデフォルトで設定されている内容になります。
10000回/s、バースト5000で設定されておりこの値より小さな値に変更することはできません。
リクエスト数の上限の緩和に関しては、クォータの引き上げが可能です。
バーストに関しては変更を要請することはできません。
詳細に関しては以下をご参照ください。
https://docs.aws.amazon.com/apigateway/latest/developerguide/limits.html
APIキーと使用量プランに基づくスロットリング
こちらが一般的によく知られたスロットリングかと思います。
APIキーと使用量プランを組み合わせることにより、特定のAPIキーに対してスロットリングを行うことができます。
こちらの設定では、APIキーを使ったアクセス全体に対するスロットリングとメソッドに対して個別でスロットリングを行うことができます。
アクセス全体に対しては秒間のスロットリングと日・週・月単位でのクォーターと
各メソッド個別のスロットリングを設定できます。
アクセス全体に対するメソッドレベルでのスロットリング
ステージングされているメソッドごとに各自でスロットリングの制限を持つことができます。
またステージ単位でデフォルトのスロットリングの値を設定することができ、個別の値を設定しない際はこちらの値が適用されるようになっています。
こちらの設定もあくまでメソッドのスロットリングのデフォルト値となります。
メソッド単位ではスロットリングの設定が可能ですが、あくまでメソッド個別での設定となるため作成したステージ全体を単位としたスロットリングの設定を行うことはできません。
やってみた
では実際にこれらを設定して試していきたいと思います。 今回はAPIキーと使用量プランに基づくスロットリングとアクセス全体に対するメソッドレベルでのスロットリングについて検証してみたいと思います。
今回負荷ツールとしてvegetaを利用しています。
APIキーと使用量プランに基づくスロットリング
アクセス全体に対する設定
まずは全体のアクセスに対するスロットリングを設定します。2種類の設定を作成して設定通りの挙動となるか確認していきます。
無料用を想定したAPIキーには、5回/sのスロットリング・バースト値10を設定した使用量プランと紐付け、
有料用を想定したAPIキーには。50回/sのスロットリング・バースト値10を設定した使用プランと紐付けを行います。
使用量プランの設定以外は同じ条件で、10回/sのアクセスを10秒間行なって、スロットリングが正しく行われているか確認します。
無料プランではスロットリングの倍のアクセスを行うため、半分ほどがエラーとなる想定です。
% echo "GET https://XXX.example.com/test/first" | vegeta attack -header "X-API-KEY:XXXXXXXXXXXX" -rate=10 -duration=10s | tee results.bin | vegeta report
Requests [total, rate, throughput] 100, 10.10, 5.24 Duration [total, attack, wait] 9.917s, 9.899s, 17.875ms Latencies [min, mean, 50, 90, 95, 99, max] 13.904ms, 34.313ms, 30.077ms, 45.479ms, 49.662ms, 249.894ms, 306.968ms Bytes In [total, mean] 4244, 42.44 Bytes Out [total, mean] 0, 0.00 Success [ratio] 52.00% Status Codes [code:count] 200:52 429:48 Error Set: 429 Too Many Requests
想定通り、約半数が429エラーとなりました。
有料プランではスロットリングの設定内のアクセスであるため、エラーは発生しない想定です。
% echo "GET https://XXX.example.com/test/first" | vegeta attack -header "X-API-KEY:XXXXXXXXXXXX" -rate=10 -duration=10s | tee results.bin | vegeta report
Requests [total, rate, throughput] 100, 10.10, 10.06 Duration [total, attack, wait] 9.944s, 9.9s, 44.145ms Latencies [min, mean, 50, 90, 95, 99, max] 29.189ms, 77.836ms, 41.584ms, 58.214ms, 370.434ms, 779.06ms, 820.243ms Bytes In [total, mean] 5300, 53.00 Bytes Out [total, mean] 0, 0.00 Success [ratio] 100.00% Status Codes [code:count] 200:100
想定通り、エラーの発生することなく実行をすることができました。
使用量プランの設定によって、APIキー毎にスロットリングの制御ができることを確認できました。
では続けてクォータについても確認していきます。
無料用を想定したAPIキーには、100回/日を設定した使用量プランと紐付け、
有料用を想定したAPIキーには、1000回/日を設定した使用プランと紐付けを行います。
使用量プランのAPIキー作成し直しそれぞれの実行回数が0となっていることを確認します。
使用量プランの設定以外は同じ条件でそれぞれに150回ずつアクセスを行います。
% echo "GET https://XXX.example.com/test/first" | vegeta attack -header "X-API-KEY:XXXXXXXXXXXX" -rate=10 -duration=15s | tee results.bin | vegeta report
Requests [total, rate, throughput] 150, 10.07, 6.70 Duration [total, attack, wait] 14.918s, 14.899s, 18.283ms Latencies [min, mean, 50, 90, 95, 99, max] 15.85ms, 43.774ms, 42.913ms, 53.689ms, 67.567ms, 272.053ms, 313.907ms Bytes In [total, mean] 6700, 44.67 Bytes Out [total, mean] 0, 0.00 Success [ratio] 66.67% Status Codes [code:count] 200:100 429:50 Error Set: 429 Too Many Requests
100回目以降は想定通りアクセスできないことを確認しました。
先ほどのカウンターを確認すると100件のアクセスがあったことを確認できました。
続いて有料プランです。
% echo "GET https://XXX.example.com/test/first" | vegeta attack -header "X-API-KEY:XXXXXXXXXXXX" -rate=10 -duration=15s | tee results.bin | vegeta report
Requests [total, rate, throughput] 150, 10.07, 10.03 Duration [total, attack, wait] 14.96s, 14.9s, 59.922ms Latencies [min, mean, 50, 90, 95, 99, max] 28.09ms, 43.096ms, 39.922ms, 47.795ms, 57.832ms, 166.984ms, 258.424ms Bytes In [total, mean] 7950, 53.00 Bytes Out [total, mean] 0, 0.00 Success [ratio] 100.00% Status Codes [code:count] 200:150 Error Set:
こちらはエラーなくアクセスできていたことを確認できました。
クォータに関しても、APIキーによって個別で制限を行うことができることを確認しました。
クォータに関してですが、APIキーと同一のステージを含んだ使用量プランが一つしか結び付けられないため、1000回/日と5000回/週の組み合わせといった複数のクォータを設定することはできませんでした。
日・週・月のいずれか1つのみしか設定が行えませんので、クォータで制限を考えている際には注意が必要になります。
最後にAPIキーに対するアクセス全体に対して、スロットリングができているか検証していきます。
まず先ほどと同様に単一のメソッドに対して、アクセスを行なってエラーが発生しないことを確認します。
使用量プランの設定がこちらになります。
以下の値で実行して、エラーが発生していないことを確認します。
% echo "GET https://XXX.example.com/test/first" | vegeta attack -header "X-API-KEY:XXXXXXXXXXXX" -rate=10 -duration=15s | tee results.bin | vegeta report
Requests [total, rate, throughput] 150, 10.07, 10.04 Duration [total, attack, wait] 14.938s, 14.9s, 38.405ms Latencies [min, mean, 50, 90, 95, 99, max] 28.681ms, 41.134ms, 39.423ms, 46.707ms, 51.042ms, 116.831ms, 208.676ms Bytes In [total, mean] 7950, 53.00 Bytes Out [total, mean] 0, 0.00 Success [ratio] 100.00% Status Codes [code:count] 200:150 Error Set:
単一で実行した場合問題ないことが確認できました。
次は以下の同じAPIキーを利用した別のメソッドへのアクセスを二つを同時に実行して、スロットリングするかを確認します。
% echo "GET https://XXX.example.com/test/first" | vegeta attack -header "X-API-KEY:XXXXXXXXXXXX" -rate=10 -duration=15s | tee results.bin | vegeta report
% echo "GET https://XXX.example.com/test/second" | vegeta attack -header "X-API-KEY:XXXXXXXXXXXX" -rate=10 -duration=15s | tee results.bin | vegeta report
結果として多少のバラツキは出ましたが、スロットリングを行なってくれることを確認できました。
Requests [total, rate, throughput] 150, 10.07, 6.69 Duration [total, attack, wait] 14.941s, 14.9s, 41.148ms Latencies [min, mean, 50, 90, 95, 99, max] 13.523ms, 33.091ms, 33.492ms, 43.191ms, 47.117ms, 112.638ms, 224.009ms Bytes In [total, mean] 6850, 45.67 Bytes Out [total, mean] 0, 0.00 Success [ratio] 66.67% Status Codes [code:count] 200:100 429:50 Error Set: 429 Too Many Requests
Requests [total, rate, throughput] 150, 10.07, 7.37 Duration [total, attack, wait] 14.922s, 14.899s, 23.229ms Latencies [min, mean, 50, 90, 95, 99, max] 14.19ms, 36.211ms, 35.712ms, 46.535ms, 50.789ms, 113.324ms, 204.914ms Bytes In [total, mean] 7070, 47.13 Bytes Out [total, mean] 0, 0.00 Success [ratio] 73.33% Status Codes [code:count] 200:110 429:40 Error Set: 429 Too Many Requests
これで同じAPIキーを利用して、別々のメソッドにアクセスしてもAPIキー全体でスロットリングを行えることを確認することができました。
メソッドごとの個別の設定
ここまでは全体に対してのスロットリングの設定を確認してきました。
ここからはメソッドで個別で行う設定を確認していきたいと思います。
個別の設定は以下画像の赤枠内から行います。なお全体の設定が今回の検証に影響しないように値の引き上げをおこなっています。
今回は以下の個別設定を行ないました。
先ほどまでと同様に同じ条件で/firstと/secondに負荷を与えていきたいと思います。
まずは/firstを試していきます。こちらは低い値を設定しているので想定通りならスロットリングされる想定です。
% echo "GET https://XXX.example.com/test/first" | vegeta attack -header "X-API-KEY:XXXXXXXXXXXX" -rate=5 -duration=15s | tee results.bin | vegeta report
Requests [total, rate, throughput] 75, 5.07, 3.24 Duration [total, attack, wait] 14.837s, 14.8s, 37.012ms Latencies [min, mean, 50, 90, 95, 99, max] 13.329ms, 37.346ms, 35.312ms, 47.688ms, 56.009ms, 249.861ms, 297.975ms Bytes In [total, mean] 3381, 45.08 Bytes Out [total, mean] 0, 0.00 Success [ratio] 64.00% Status Codes [code:count] 200:48 429:27 Error Set: 429 Too Many Requests
想定通り、スロットリングされることを確認しました。
続いて/secondを試していきます。こちらはエラーが発生しない想定です。
% echo "GET https://XXX.example.com/test/second" | vegeta attack -header "X-API-KEY:XXXXXXXXXXXX" -rate=5 -duration=15s | tee results.bin | vegeta report
Requests [total, rate, throughput] 75, 5.07, 5.05 Duration [total, attack, wait] 14.842s, 14.8s, 41.732ms Latencies [min, mean, 50, 90, 95, 99, max] 27.744ms, 41.795ms, 38.434ms, 45.006ms, 49.916ms, 211.367ms, 263.396ms Bytes In [total, mean] 3975, 53.00 Bytes Out [total, mean] 0, 0.00 Success [ratio] 100.00% Status Codes [code:count] 200:75 Error Set:
こちらは想定通りエラーが発生しませんでした。
以上のようにメソッドで個別にスロットリングができることも確認できました。
最後に全体<個別となるようなスロットリングの設定を行なった際の挙動を確認したいと思います。
全体の設定を以下に。
メソッド個別の設定を以下にします。
メソッド個別の設定が適用される場合は、エラーが発生しない想定の値で負荷をかけてみます。
% echo "GET https://XXX.example.com/test/first" | vegeta attack -header "X-API-KEY:XXXXXXXXXXXX" -rate=5 -duration=15s | tee results.bin | vegeta report
Requests [total, rate, throughput] 150, 10.07, 5.29 Duration [total, attack, wait] 14.945s, 14.9s, 44.966ms Latencies [min, mean, 50, 90, 95, 99, max] 11.182ms, 39.32ms, 32.33ms, 41.825ms, 45.243ms, 470.88ms, 587.716ms Bytes In [total, mean] 6388, 42.59 Bytes Out [total, mean] 0, 0.00 Success [ratio] 52.67% Status Codes [code:count] 200:79 429:71 Error Set: 429 Too Many Requests
結果としては、メソッド個別の値よりも全体の設定が優先されることを確認することができました。
全体のアクセスの想定を上回る、メソッド個別のアクセスはスロットリングされるべきですので、当然といったところでしょうか。
個別の設定はあくまでも全体の設定の中で、メソッド個別に振り分けて作成したい際に利用するものといった感じかと思います。
このような設定となるケースはないかと思いますが、全体の設定を念頭に個別のメソッドの設定を行う必要がありそうです。
アクセス全体に対するメソッドレベルでのスロットリング
今までの検証は使用量プランに紐づいたスロットリングについて確認してきました。 ここからはステージングされているメソッド全体に対するスロットリングを検証していきます。
APIキーなしでのアクセスを許容するのは望ましくないため、引き続きAPIキーを設定し使用量プランを検証に影響ない値に変更して検証を行なっていきます。
まずは個別のスロットリングが機能するか確認していきたいと思います。
全体の設定を以下のようにして
設定を上回る負荷をかけます。
% echo "GET https://XXX.example.com/test/first" | vegeta attack -header "X-API-KEY:XXXXXXXXXXXX" -rate=10 -duration=15s | tee results.bin | vegeta report
Requests [total, rate, throughput] 150, 10.07, 5.09 Duration [total, attack, wait] 14.917s, 14.9s, 16.771ms Latencies [min, mean, 50, 90, 95, 99, max] 12.93ms, 53.716ms, 31.344ms, 42.794ms, 185.037ms, 792.709ms, 861.547ms Bytes In [total, mean] 6322, 42.15 Bytes Out [total, mean] 0, 0.00 Success [ratio] 50.67% Status Codes [code:count] 200:76 429:74 Error Set: 429 Too Many Requests
無事スロットリングされたことを確認できました。
念の為別のメソッドに影響を与えていないか確認してみます。
デフォルトの設定を引き継いだ/secondに同様の負荷を同時にかけてみます。
% echo "GET https://XXX.example.com/test/second" | vegeta attack -header "X-API-KEY:XXXXXXXXXXXX" -rate=10 -duration=15s | tee results.bin | vegeta report
Requests [total, rate, throughput] 150, 10.07, 10.04 Duration [total, attack, wait] 14.938s, 14.9s, 38.122ms Latencies [min, mean, 50, 90, 95, 99, max] 25.927ms, 40.175ms, 35.798ms, 41.688ms, 47.127ms, 223.799ms, 322.87ms Bytes In [total, mean] 7950, 53.00 Bytes Out [total, mean] 0, 0.00 Success [ratio] 100.00% Status Codes [code:count] 200:150 Error Set:
影響が出ていないことを確認できました。
使用量プランを設定していなくても、このようにメソッド個別にスロットリングを設定することができました。
最後に
今回はAPI Gatewayのスロットリング機能の検証を行なってみました。複数箇所でスロットリングの設定ができますので組み合わせれば様々な要件に対応できそうなことがわかりました。この記事がどなたかのお役に立てたことを祈っています。